package com.mg.plugin.agrometeorology.model;

import com.mg.model.Model;
import com.mg.model.Param;
import com.mg.model.Port;
import com.mg.plugin.agrometeorology.common.*;
import com.mg.plugin.agrometeorology.entity.StationData;
import com.mg.pluginexample2.ModuleBase;

import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

public class ProductionForecastModel extends ModuleBase {
    public ProductionForecastModel(int nID) {
        super(nID);
        m_alInputPort.add(new Port(this, Port.Type.Unknown));
        m_alInputPort.add(new Port(this, Port.Type.Unknown));
        m_alOutputPort.add(new Port(this, Port.Type.Unknown));
    }

    public String GetGroupName() {
        return "产量预报";
    }

    public String GetName() {
        return "相关因子值计算";
    }

    public String GetDescription() {
        return "相关因子值计算（数据必须是同一年的）";
    }

    public boolean OnAttach(Port portFrom, Port portTo) {
        try {
            int i = FindPort(portFrom, false);
            if (i != -1) {
                return true;
            }
            i = FindPort(portTo, true);
            if (i == -1) {
                return false;
            }

            ArrayList<Param> alParam = new ArrayList<Param>();
            portFrom.GetModule().GetOutputParam(portFrom, alParam);

            if (alParam.size() != 1) {
                return false;
            }

            if (alParam.get(0).m_objValue != null && !(alParam.get(0).m_objValue instanceof ArrayList)) {
                return false;
            }
            if (i == 0) {
                m_inputStationDataList = (List<String>) alParam.get(0).m_objValue;
            }
            if (i == 1) {
                m_inputStationDataList1 = (List<String>) alParam.get(0).m_objValue;
            }

            portTo.SetType(portFrom.GetType());
        } catch (Exception ex) {
            ex.printStackTrace();
            return false;
        }
        return true;
    }

    public boolean OnDetach(Port portFrom, Port portTo) {
        if (!super.OnDetach(portFrom, portTo))
            return false;
        try {
            int i = FindPort(portTo, true);
            if (i == -1)
                return true;
            return true;
        } catch (Exception ex) {
            ex.printStackTrace();
            return false;
        }
    }

    //用于 参数->XML 等
    public int GetParam(ArrayList<Param> alParam) {
        try {
            super.GetParam(alParam);
            alParam.add(new Param("StationId", m_strStationId, "站点ID对应字段", "StationId", "参数匹配", Param.EditType.Default));
            alParam.add(new Param("Date", m_strDate, "时间对应字段", "Date", "参数匹配", Param.EditType.Default));
            alParam.add(new Param("Precipitation", m_strRain, "降水对应字段", "Precipitation", "参数匹配", Param.EditType.Default));
            alParam.add(new Param("MaxTemperature", m_strTmax, "最高温对应字段", "MaxTemperature", "参数匹配", Param.EditType.Default));
            alParam.add(new Param("MinTemperature", m_strTmin, "最低温对应字段", "MinTemperature", "参数匹配", Param.EditType.Default));
            alParam.add(new Param("SunshineHour", m_strSun, "日照时数对应字段", "SunshineHour", "参数匹配", Param.EditType.Default));
            alParam.add(new Param("m_strStartTime", m_strStartTime, "数据开始时间", "Time", "时间", Param.EditType.Default));
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return alParam.size();
    }

    public boolean OnParamChanged(ArrayList<Param> alParam, int nIndex, Object objValue) {
        return super.OnParamChanged(alParam, nIndex, objValue);
    }

    //用于 XML->参数 等
    public int SetParam(final ArrayList<Param> alParam) {
        int i = super.SetParam(alParam);
        try {
            m_strStationId = (String) alParam.get(i++).m_objValue;
            m_strDate = (String) alParam.get(i++).m_objValue;
            m_strRain = (String) alParam.get(i++).m_objValue;
            m_strTmax = (String) alParam.get(i++).m_objValue;
            m_strTmin = (String) alParam.get(i++).m_objValue;
            m_strSun = (String) alParam.get(i++).m_objValue;
            m_strStartTime = (String) alParam.get(i++).m_objValue;
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return i;
    }

    public int GetOutputParam(Port port, ArrayList<Param> alParam) {
        int i = FindPort(port, false);
        if (i == -1) {
            return 0;
        }

        alParam.add(new Param("ResultString", m_listCSV));
        return alParam.size();
    }

    @SuppressWarnings("static-access")
    public boolean Execute() {

        m_listCSV.clear();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        String head = "year,beginDate,endDate,accTemp,staRain,accSun,tAvgTemp,tAccRain,tAccSun,suitRiceTemp,suitCornTemp,suitBeanTemp,suitRain,suitSun,argRiceSuit,argCornSuit,argBeanSuit";
        String type = "String,String,String,Double,Double,Double,Double,Double,Double,Double,Double,Double,Double,Double,Double,Double,Double";
        m_listCSV.add(head);
        m_listCSV.add(type);

        Map<String, List<StationData>> stationDataList = new HashMap<String, List<StationData>>();
        Map<String, List<StationData>> oldStationDataList = new HashMap<String, List<StationData>>();
        Map<String, List<StationData>> stationDataList2 = new HashMap<String, List<StationData>>();
        Map<String, List<StationData>> oldStationDataList2 = new HashMap<String, List<StationData>>();
        String date = "";
        if (m_inputStationDataList != null && m_inputStationDataList.size() > 0) {
            String foreHeader = m_inputStationDataList.get(0);
            if (!verifyHeader(foreHeader)) {
                m_model.OutputLog(Model.LogLevel.Error, "Header Can Not Match!");
                return false;
            } else {
                date = formatStationData(m_inputStationDataList, foreHeader, stationDataList, stationDataList2);
            }

        }

        if (m_inputStationDataList1 != null && m_inputStationDataList1.size() > 0) {
            String foreHeader = m_inputStationDataList1.get(0);
            if (!verifyHeader(foreHeader)) {
                m_model.OutputLog(Model.LogLevel.Error, "Header Can Not Match!");
                return false;
            } else {
                formatStationData(m_inputStationDataList1, foreHeader, oldStationDataList, oldStationDataList2);
            }

        }
        SimpleDateFormat sdfYear = new SimpleDateFormat("yyyy");
        String str = m_strStartTime.replace("$ThisYear", sdfYear.format(m_model.m_dateDataStart));
        try {
            for (int i = 0; i < 30; i++) {
                String calcDate = sdf.format(DateCalculator.calcTime(sdf.parse(date), i));
                float accTemp = TemperatureCalculator.calcAccumulatedTemperature(stationDataList, i, m_model);
                float staRain = PrecipitationCalculator.calcStandardizedPrecipitation(stationDataList, i, m_model);
                float accSun = SunshineCalculator.calcAccumulateHoursOfSunshine(stationDataList, i, m_model);
                float tAvgTemp = TemperatureCalculator.calcTendaysAverageTemperature(stationDataList, i, m_model);
                float tAccRain = PrecipitationCalculator.calcTendaysAccumulatePrecipitation(stationDataList, i, m_model);
                float tAccSun = SunshineCalculator.calcTendaysAccumulateHoursOfSunshine(stationDataList, i, m_model);
                float suitRiceTemp = TemperatureCalculator.calcTendaysTemperatureSuitability(stationDataList, i, true, m_model);
                float suitCornTemp = TemperatureCalculator.calcTendaysTemperatureSuitability(stationDataList, i, false, m_model);
                float suitBeanTemp = TemperatureCalculator.calcTendaysTemperatureSuitability(stationDataList, i, false, m_model);
                float suitRain = PrecipitationCalculator.calcTendaysSuitabilityPrecipitation(stationDataList, oldStationDataList, i, m_model);
                float suitSun = SunshineCalculator.calcTendaysSuitabilityHoursOfSunshine(stationDataList, oldStationDataList, i, m_model);
                double argRiceSuit = getArgSuit(suitRiceTemp, suitRain, suitSun);
                double argCornSuit = getArgSuit(suitCornTemp, suitRain, suitSun);
                double argBeanSuit = getArgSuit(suitBeanTemp, suitRain, suitSun);
                m_listCSV.add(sdfYear.format(sdf.parse(date)) + "," + str + "," + calcDate + "," + accTemp + ","
                        + staRain + "," + accSun + "," + tAvgTemp + "," + tAccRain + "," + tAccSun
                        + "," + suitRiceTemp + "," + suitCornTemp + "," + suitBeanTemp + "," + suitRain
                        + "," + suitSun + "," + argRiceSuit + "," + argCornSuit + "," + argBeanSuit);
            }

        } catch (ParseException e) {
            e.printStackTrace();
        }

        OnParamChanged();
        return true;
    }

    private double getArgSuit(float suitTemp, float suitRain, float suitSun) {
        suitTemp = new BigDecimal(suitTemp).setScale(2, BigDecimal.ROUND_HALF_UP).floatValue();
        suitRain = new BigDecimal(suitRain).setScale(2, BigDecimal.ROUND_HALF_UP).floatValue();
        suitSun = new BigDecimal(suitSun).setScale(2, BigDecimal.ROUND_HALF_UP).floatValue();
        double res = suitRain * suitSun * suitTemp;
        res = Math.pow(res, 1 / 3.0);
        if (Double.isNaN(res)) {
            m_model.OutputLog(Model.LogLevel.Info, "suitRain * suitSun * suitTemp=" + suitRain + "*" + suitSun + "*" + suitTemp);
            return suitRain * suitSun * suitTemp;
        }
        return Math.pow(suitRain * suitSun * suitTemp, 1 / 3.0);
    }


    private String formatStationData(List<String> inputRealityList, String realHeader, Map<String, List<StationData>> stationDataList, Map<String, List<StationData>> stationDataList2) {
        int realStationIndex = findDataIndex(realHeader, m_strStationId);
        int realDateIndex = findDataIndex(realHeader, m_strDate);
        int realRainIndex = findDataIndex(realHeader, m_strRain);
        int realTmaxIndex = findDataIndex(realHeader, m_strTmax);
        int realTminIndex = findDataIndex(realHeader, m_strTmin);
        int realSunIndex = findDataIndex(realHeader, m_strSun);
        String date = "";
        for (int i = 2; i < inputRealityList.size(); i++) {
            String[] data = inputRealityList.get(i).split(",");
            String stationId = data[realStationIndex];
            date = data[realDateIndex];
            StationData stationData = new StationData(stationId);
            stationData.setDate(data[realDateIndex]);
            stationData.setRain(Float.parseFloat(data[realRainIndex]));
            stationData.setMaxTemp(Float.parseFloat(data[realTmaxIndex]));
            stationData.setMinTemp(Float.parseFloat(data[realTminIndex]));
            stationData.setSunshine(Float.parseFloat(data[realSunIndex]));

            addToMap(stationDataList, data[realDateIndex], stationData);

            addToMap(stationDataList2, stationId, stationData);
        }
        return date;
    }

    private void addToMap(Map<String, List<StationData>> stationDataList2, String stationId, StationData stationData) {
        if (stationDataList2.keySet().contains(stationId)) {
            stationDataList2.get(stationId).add(stationData);
        } else {
            List<StationData> dataList = new ArrayList<StationData>();
            dataList.add(stationData);
            stationDataList2.put(stationId, dataList);
        }
    }


    /**
     * 表头检验
     *
     * @param realHeader 实况数据表头
     * @return
     */
    private boolean verifyHeader(String realHeader) {

        if (realHeader.contains(m_strDate) && realHeader.contains(m_strStationId)) {
            if (realHeader.contains(m_strTmax) && realHeader.contains(m_strTmin)
                    && realHeader.contains(m_strRain) && realHeader.contains(m_strSun)) {
                return true;
            }
        }
        return false;
    }

    private int findDataIndex(String realHeader, String strRain) {

        String[] data = realHeader.split(",");
        for (int i = 0; i < data.length; i++) {
            if (data[i].equalsIgnoreCase(strRain)) {
                return i;
            }
        }
        return -1;
    }

    private List<String> m_inputStationDataList = new ArrayList<String>();
    private List<String> m_inputStationDataList1 = new ArrayList<String>();
    private List<String> m_listCSV = new ArrayList<String>();

    private String m_strStationId;
    private String m_strDate;
    private String m_strRain;
    private String m_strTmax;
    private String m_strTmin;
    private String m_strSun;
    private String m_strStartTime;
}
